# -*- coding: utf-8 -*-
"""
@author: luhx
@file: strategy_auto_close_position_imp.py
@time: 2023/8/1 6:04
@desc: 自动平仓单，检测系统账户，若存在订单，
多单看死叉平仓，空单看金叉平仓
"""
import string
from base.kBase import kBase, Contract
from kds_util.user_logbook import init_logger as init_logs, user_log as logger, system_log
from highlow import algo
from highlow.dd_bs import DdBs
from highlow.dd_bkinfo import DDParam
from highlow.dd_obj import BkItem, TBkId, ESignalType, HoldItem
from highlow.envs import Envs
from typing import Dict, Any
from highlow.database_opt import create_strategy_high_low_table
from base.BaseCD import MA, D_MA
from datetime import timedelta
from datetime import datetime
from highlow.business import Business


class StrategyAutoClosePositionImp(kBase):
    def __init__(self, investor_id: str, days=100, mindays=7, stime="", etime="", codes=[]):
        logger.info("系统正在数据初始化，请稍后...")
        super().__init__(investor_id, days, mindays, stime, etime, codes, playback=Envs.dest_play_back, url=Envs.dest_url, funcs=self.UpdateUsrParam)
        bs = DdBs(self)
        logger.info("系统初始化完成.")
        Envs.data_center_ip = self.data_center_ip
        self.bs: DdBs = bs
        self.mysql = bs.mysql
        create_strategy_high_low_table(bs.mysql)
        self.UsrParams: Dict[str, DDParam] = {}
        self.bs.dd_account.params = self.bs.params = self.bs.bk_info.params = self.bs.trader.params = self.UsrParams
        for key in self.allContract:
            self.UsrParams[key] = DDParam(self.allContract[key])
        for key in self.Breed2Symbol.keys():
            l6 = key + "L6"
        self.is_inited = True
        self.business = Business(self)

    def UpdateUsrParam(self, code: str):
        self.UsrParams[code] = DDParam(self.allContract[code])
        return

    def reset_all_day_status(self):
        for k in self.UsrParams.keys():
            self.UsrParams[k].reset_new_one_day()
        self.bs.bk_info.bk_items = {}

    def reset_hq_status(self):
        for k in self.UsrParams.keys():
            self.UsrParams[k].set_hq_status(False)

    def reset_night_trading_status(self):
        """
        重置夜盘的状态
        :return:
        """
        trade_time = self.trade_time
        if trade_time[-8:] == "02:30:00":
            logger.info(f"[{self.trade_time}]:夜盘时间结束，后面的为白盘...")
            for k in self.UsrParams.keys():
                self.UsrParams[k].reset_status()
            for bk_code in self.bs.bk_info.bk_items.keys():
                self.bs.bk_info.bk_items[bk_code].trade_type = ""

    def is_same_white_or_night(self):
        if Envs.dest_play_back:  # 如果是回放，不再管是白盘还是夜盘
            return True
        white_plate = Envs.white_plate_flag and self.bs.is_kline_white_market()
        night_plate = not Envs.white_plate_flag and not self.bs.is_kline_white_market()
        return white_plate or night_plate

    def clear_all_position(self):
        """
        清仓所有合约
        @return:
        """
        holds = self.bs.dd_account.get_holds()
        for hold_item in holds.values():
            self.bs.trader.opt_clear_position(hold_item, self.mysql)

    def run_day_kline(self, m: Contract):  # 每个日线串行回调
        if m.klines1d.id >= len(m.klines1d.klines):
            logger.warn(f"{m.codeL}.{m.code}: m.klines1d.id:{m.klines1d.id} >= m.klines1d.id({len(m.klines1d.klines)})")
            return
        if self.bs.check_cur_trading_day_end():  # 如果当前交易日也走完了，直接退出
            # logger.warn(f"cur_trading_day_is_over. cur_trade_day: {Envs.cur_trade_day}")
            if Envs.dest_play_back:
                pass
            else:
                return
        k = m.klines1d.klines[m.klines1d.id]
        p: DDParam = self.UsrParams[m.codeL]
        p.minute1_kline = k
        p.count1m = 0
        p.Atr.input(k)  # 波动
        p.ma5.input(k.close)
        if m.typ == "L9":
            p.MACDDiver.input(k)
        # p.dma5 = avlist([x.close for x in m.klines1d.last_n_klines(5)])  # 5日均
        # if self.bs.check_prev_trading_day_end():
        if m.typ == "L9":
            p.dma5 = D_MA(5)
            for kline in p.contract_info.klines1d.last_n_klines(4):
                p.dma5.input(kline.close)
        return

    def run_day_kline2(self):
        # 日线跑完，全部回调  {筛票阶段}
        if self.bs.check_prev_trading_day_end():
            if self.bs.check_cur_trading_day_end():  # 如果当前交易日也走完了，直接退出
                logger.warn(f"cur_trading_day_is_over. cur_trade_day: {Envs.cur_trade_day}")
                if Envs.dest_play_back:
                    new_trading_day = algo.get_next_trading_day(self.bs.bk_info.pd_days, Envs.cur_trade_day)
                    new_dt = datetime.strptime(str(new_trading_day), "%Y%m%d")
                    new_dt += timedelta(hours=16)
                    new_date_time = new_dt.strftime("%Y-%m-%d %H:%M:%S")
                    algo.init_trade_day(self.bs.bk_info.pd_days, spec_time=new_date_time)
                    self.init_daily_level_data()  # 重新初始化日线级数据
                    self.init_cycle_level_data()
                else:
                    pass
            else:
                if not Envs.dest_play_back:  # 不是回放，更新一下状态
                    self.GetAccontInfo()
                self.init_daily_level_data()
                self.init_cycle_level_data()
        return

    def init_daily_level_data(self):
        logger.info(f"[{self.trade_time}]:系统运行起始日，开始：{Envs.bk_end_time}--》结束：{Envs.will_end_time}")
        self.reset_all_day_status()  # 重新

    def init_cycle_level_data(self):
        """
        初始化周期的数据
        @return:
        """
        logger.info(f"[{self.trade_time}]:初始化周期信号")
        self.bs.bk_info.init_bk_item_from_dd_signal(self.bs.params)  # 初始化板块顶底信号，每个板块从最强的选出一个来
        self.bs.bk_info.init_bk_item_from_hold(self.bs.bk_info, self.bs.dd_account)  # 合并持仓数据到信号中
        self.force_trade_able_all()
        # self.bs.log_signal_info()

    def run_minute_kline(self, m: Contract):  # 分钟线回调，计算个票{信号阶段}
        if m.klines1m.id >= len(m.klines1m.klines):
            logger.warn(
                f"{m.codeL}.{m.code}: m.klines1m.id:{m.klines1m.id} >= len(m.klines1m.id)({len(m.klines1m.klines)})")
            return
        # if m.code == "wenhuashangpin":
        #     logger.info(f"wenhuashangpin:[{self.trade_time}]")
        k = m.klines1m.klines[m.klines1m.id]
        p: DDParam = self.UsrParams[m.codeL]
        p.day_kline.appendkline(k)  # 合成日线
        p.count1m += 1
        p.jx_sx.input(k.close)

        p.minute1_kline = k  # 合成分钟线
        p.turnover += k.volume * (k.open + k.high + k.low + k.close) / 4
        p.volume += k.volume
        p.mav = p.turnover / p.volume if p.volume else k.close  # 分钟线生成的日内均线
        p.dir = 1 if k.close > p.mav else -1
        p.set_hq_status(True)  # 标识当前分钟有行情
        signal_status = p.DbMa.double_ma_input(k.close)
        if (signal_status == 1 or signal_status == -1) and \
            ((Envs.dest_codes and p.contract_info.breed in Envs.dest_codes) or not Envs.dest_codes) \
            and p.contract_info.typ == "L6":
            logger.info(f"[{self.trade_time}]:has_signal : {p.contract_info.codeL} : {signal_status}")

        p.dma5.input_s(k.close)
        p.MACDBS.input(k)
        if not self.bs.prev_day_end:  # 对于分钟数据，只有到达新交易日了才能使用
            return
        return

    def force_trade_able_all(self):
        for key in self.UsrParams.keys():
            self.UsrParams[key]._force_trade = True

    def whole_n_minute(self, minute=15) -> bool:
        """
        判断系统时间是否为整minute(15)分钟
        @param minute:
        @return:
        """
        rel = ((self.intTradeTime // 100) % 100) % minute == 5
        return rel

    def run_minute_kline2(self):  # 每分钟跑完，回调，计算票和票、票和板块之间关系阶段，修正个票信号
        if not self.bs.prev_day_end:
            return
        self.bs.dd_account.update_account_status(self.bs.dd_account.contract_opt_positions)  # 更新账户资金及持仓信息
        holds = self.bs.dd_account.get_holds()
        if self.RealCount > 0:  # 实盘才能交易，否则需跳过交易环节
            self.wait_signal_clear_position(holds)  # 等待信号并从持仓中将信号相反的持仓清空
            self.wait_signal_open_position(holds)   # 等待信号并从票池中取票开仓
        self.reset_night_trading_status()
        self.reset_hq_status()  # 将每个品种的行情重新置为不可用

    def wait_signal_clear_position(self, holds):
        """等待信号并清仓"""
        for breed in holds.keys():
            p6: DDParam = self.UsrParams.get(breed + "L6", None)
            if p6 is None:
                continue
            hold_item: HoldItem = holds.get(breed, None)
            if self.business.has_position(hold_item, -1):
                if p6.DbMa.buy_sell == 1:
                    self.bs.trader.opt_clear_side_position(hold_item, -1, self.mysql)
            if self.business.has_position(hold_item, 1):
                if p6.DbMa.buy_sell == -1:
                    self.bs.trader.opt_clear_side_position(hold_item, 1, self.mysql)
                    logger.info(f"[{p6.contract_info.trade_time}]:{p6.DbMa}")
                    pass

    def wait_signal_open_position(self, holds):
        """等待信号并开仓"""
        pass
